package org.mm.proxycache.http.codec;

import static com.google.common.base.Objects.nonNull;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.mm.proxycache.http.HttpVersion;
import org.mm.proxycache.http.MutableHttpMessage;
import org.mm.proxycache.util.CaseInsensitiveComparator;

import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.sleepycat.persist.model.Persistent;

@Persistent
public class DefaultHttpMessage implements MutableHttpMessage
{
    Multimap<String, String> headers;
    
    HttpVersion httpVersion;
    
    private AtomicLong realSize = new AtomicLong();
    
    protected DefaultHttpMessage()
    {
        headers = Multimaps.newTreeMultimap( new CaseInsensitiveComparator(), null );
        httpVersion = HttpVersion.Http11;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#containsHeader(java.lang.String)
     */
    public boolean hasHeader( String name )
    {
        checkNotNull( name );
        
        return headers.containsKey( name );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#getContentType()
     */
    public String contentType()
    {
        return header( "Content-Type" );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#getHeader(java.lang.String)
     */
    public String header( String name )
    {
        checkNotNull( name );
        
        Collection<String> strings = headers.get( name );
        if ( strings == null || strings.isEmpty() )
            return null;
        return strings.iterator().next();
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#getHeaders()
     */
    public Multimap<String, String> getHeaders()
    {
        return headers;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#getProtocolVersion()
     */
    public HttpVersion version()
    {
        return httpVersion;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#isKeepAlive()
     */
    public boolean isKeepAlive()
    {
        String connection = header( "Connection" );
        
        if ( version() == HttpVersion.Http11 )
            return !"Close".equalsIgnoreCase( connection );
        
        return "Keep-Alive".equalsIgnoreCase( connection );
    }
    
    public boolean isProxyKeepAlive()
    {
        boolean keepAlive = false;
        if ( httpVersion == HttpVersion.Http11 )
            keepAlive = true;
        
        String connection = header( "Proxy-Connection" );
        if ( connection != null )
            keepAlive = "keep-alive".equalsIgnoreCase( connection );
        else
        {
            connection = header( "Connection" );
            if ( connection != null )
                keepAlive = "keep-alive".equalsIgnoreCase( connection );
        }
        
        return keepAlive;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#addHeader(java.lang.String,
     *      java.lang.String)
     */
    public void addHeader( String name, String value )
    {
        checkNotNull( name );
        checkNotNull( value );
        
        headers.put( name, value );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#removeHeader(java.lang.String)
     */
    public void removeHeader( String name )
    {
        checkNotNull( name );
        
        headers.removeAll( name );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#setContentType(java.lang.String)
     */
    public void contentType( String type )
    {
        checkNotNull( type );
        
        headers.put( "Content-Type", type );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#setHeader(java.lang.String,
     *      java.lang.String)
     */
    public void header( String name, String value )
    {
        checkNotNull( name );
        checkNotNull( value );
        
        headers.removeAll( name );
        headers.put( name, value );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#setHttpVersion(org.apache.mina.filter.codec.http.HttpVersion)
     */
    public void version( HttpVersion httpVersion )
    {
        this.httpVersion = nonNull( httpVersion );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#setKeepAlive(boolean)
     */
    public void keepAlive( boolean keepAlive )
    {
        header( "Connection", keepAlive ? "Keep-Alive" : "Close" );
    }
    
    public long contentLength()
    {
        try
        {
            return Long.parseLong( header( "Content-Length" ) );
        }
        catch (NumberFormatException e)
        {
            return 0;
        }
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#isChunked()
     */
    public boolean isChunked()
    {
        return "chunked".equalsIgnoreCase( header( "Transfer-Encoding" ) );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#getCacheControl()
     */
    public Map<String, String> cacheControl()
    {
        return parseCommaSeparatedValue( "Cache-Control" );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#getKeepAliveValues()
     */
    public Map<String, String> keepAliveValues()
    {
        return parseCommaSeparatedValue( "Keep-Alive" );
    }
    
    public Map<String, String> parseCommaSeparatedValue( String headerName )
    {
        Map<String, String> control = Maps.newTreeMap();
        if ( !hasHeader( headerName ) )
            return control;
        
        for ( String s : header( headerName ).split( " " ) )
            for ( String t : s.split( "," ) )
            {
                String[] list = t.split( "=", 2 );
                if ( list.length == 2 )
                    control.put( list[0].toLowerCase(), list[1] );
                else
                    control.put( list[0].toLowerCase(), "" );
            }
        
        return control;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#addChunkSize(int)
     */
    public void addChunkSize( int size )
    {
        realSize.addAndGet( size );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#realSize()
     */
    public long realSize()
    {
        return realSize.get();
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.MutableHttpMessage#setContentLength(int)
     */
    public void contentLength( int length )
    {
        header( "Content-Length", Integer.toString( length ) );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see org.mm.proxycache.http.HttpMessage#setCurrentDate()
     */
    public void setCurrentDate()
    {
        header( "Date", HttpDateFormat.getCurrentHttpDate() );
    }
    
}
